package com.legal.aid.app.service.impl;

import com.alibaba.fastjson2.JSON;
import com.legal.aid.app.domain.doc.NewsDoc;
import com.legal.aid.app.domain.vo.NewsVo;
import com.legal.aid.app.mapper.AppCollectMapper;
import com.legal.aid.app.mapper.AppNewsMapper;
import com.legal.aid.app.service.IAppNewsService;
import com.legal.aid.common.constant.CacheConstants;
import com.legal.aid.common.constant.CollectType;
import com.legal.aid.common.constant.DictConstant;
import com.legal.aid.common.core.domain.entity.CommonUser;
import com.legal.aid.common.core.domain.model.AppLoginUser;
import com.legal.aid.common.core.redis.RedisCache;
import com.legal.aid.common.exception.ServiceException;
import com.legal.aid.common.utils.SecurityUtils;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;


@Service
public class AppNewsServiceImpl implements IAppNewsService {

    @Autowired
    private AppNewsMapper appNewsMapper;

    @Autowired
    private AppCollectMapper appCollectMapper;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 查询新闻列表，可以根据 getNewsCategory类型查询 热点新闻类型 值为5
     *
     * @param newsVo
     * @return
     */
    @Override
    public List<NewsVo> selectNewsList(NewsVo newsVo) {
        // 先查询热点新闻 值为5 走redis缓存
        if (newsVo.getNewsCategory() != null
                && newsVo.getNewsCategory().equals(String.valueOf(DictConstant.NEWS_HOT))) {
            return getTopNewsList();
        }
        // 不是热点新闻 查询数据库，返回
        List<NewsVo> list = appNewsMapper.selectNewsList(newsVo);
        return Optional.ofNullable(list)
                .filter(l -> !l.isEmpty())
                .orElse(Collections.emptyList());
    }

    private List<NewsVo> getTopNewsList() {

        List<NewsVo> topNewsList = redisCache.getCacheObject(CacheConstants.NEWS_HOT_LIST);
        if (topNewsList == null) {
            // 缓存未命中，查询数据库
            NewsVo newsVo = new NewsVo();
            newsVo.setNewsCategory(String.valueOf(DictConstant.NEWS_HOT));
            topNewsList = appNewsMapper.selectNewsList(newsVo);

            if (!topNewsList.isEmpty()) {
                // 将热点新闻分别缓存 NEWS_HOT_LIST+ newsId
                for (NewsVo news : topNewsList) {
                    // 写入缓存
                    redisCache.setCacheObject(CacheConstants.NEWS_HOT_LIST + news.getNewsId(), news,
                            (int) CacheConstants.NEWS_HOT_EXPIRE,
                            TimeUnit.SECONDS);
                }
            }

        }
        return topNewsList;

    }

    @Override
    public NewsVo getNewsDetailByNewsId(String newsId) {
        if (newsId == null || newsId.isEmpty()) {
            return null;
        }
        // 查询是否是顶置新闻，顶置新闻在缓存中
        NewsVo news = redisCache.getCacheObject(CacheConstants.NEWS_HOT_LIST + newsId);

        if (news == null) {
            // 不是热点则查数据库
            news = appNewsMapper.selectNewsByNewsId(Long.valueOf(newsId));
            if (news == null) {
                return null;
            }
            // 判断是否是热点新闻
            if (news.getNewsCategory().equals(String.valueOf(DictConstant.NEWS_HOT))) {
                // 写入缓存
                redisCache.setCacheObject(CacheConstants.NEWS_HOT_LIST + news.getNewsId(), news,
                        (int) CacheConstants.NEWS_HOT_EXPIRE,
                        TimeUnit.SECONDS);
            }
        }
        // 不为空
        // 记录浏览数，浏览数 +1
        // 使用Redis原子操作增加计数
        String key = CacheConstants.NEWS_VIEW_COUNT_KEY_PREFIX + newsId;
        redisCache.increment(key, 1);

        // 设置过期时间
        redisCache.expire(key, CacheConstants.VIEW_COUNT_EXPIRE_TIME, TimeUnit.SECONDS);
        return news;
    }

    @Override
    public List<NewsVo> selectAttentionNewsVoList() {
        AppLoginUser appLoginUser = SecurityUtils.getAppLoginUser();
        System.out.println("当前登录用户： " + (appLoginUser != null ? appLoginUser.getUsername() : "未登录") + ", ID: "
                + (appLoginUser != null ? appLoginUser.getUserId() : "N/A"));
        CommonUser user = appLoginUser.getCommonUser();
        if (user == null) {
            return Collections.emptyList();
        }
        // System.out.println(appLoginUser);
        // 根据当前登录用户id查询关注列表
        // 查询目标id列表

        // 将 CollectType.COLLECT_TYPE_NEWS 转换为字符串
        String targetType = String.valueOf(CollectType.COLLECT_TYPE_NEWS);
        List<Long> targetIds = appCollectMapper.getTargetIdList(targetType, user.getId());
        if (targetIds == null || targetIds.isEmpty()) {
            return Collections.emptyList();
        }
        // 根据targetId 查询关注新闻
        List<NewsVo> list = appNewsMapper.selectNewsListByIds(targetIds);
        return Optional.ofNullable(list)
                .filter(l -> !l.isEmpty())
                .orElse(Collections.emptyList());
    }

    /**
     * 查询热点新闻
     *
     * @return
     */
    @Override
    public List<NewsVo> selectTopNews() {
        List<NewsVo> topNewsList = getTopNewsList();
        return Optional.ofNullable(topNewsList)
                .filter(l -> !l.isEmpty())
                .orElse(Collections.emptyList());
    }

    @Override
    public List<NewsDoc> searchNewsByKeyword(String keyword, int pageNum, int pageSize) {
        // Redis 实时统计 热门搜索
        // 用户每次搜索
        redisCache.zincrby(CacheConstants.HOT_NEWS_SEARCH_KEYWORDS, 1, keyword);

        SearchRequest searchRequest = new SearchRequest("news");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 多字段全文检索
        sourceBuilder.query(QueryBuilders.multiMatchQuery(keyword, "newsTitle", "subtitle", "newsContent"));
        sourceBuilder.from((pageNum - 1) * pageSize);
        sourceBuilder.size(pageSize);
        // 排序：先按isTop降序，再按publishTime降序
//        sourceBuilder.sort("isTop", SortOrder.DESC);
//        sourceBuilder.sort("publishTime", SortOrder.DESC);

        // 设置高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("newsTitle");
        highlightBuilder.field("subtitle");
        highlightBuilder.preTags("<em>");
        highlightBuilder.postTags("</em>");
        sourceBuilder.highlighter(highlightBuilder);

        searchRequest.source(sourceBuilder);

        try {
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            List<NewsDoc> result = new ArrayList<>();
            for (SearchHit hit : response.getHits().getHits()) {
                NewsDoc newsDoc = JSON.parseObject(hit.getSourceAsString(), NewsDoc.class);

                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField titleHighlight = highlightFields.get("newsTitle");
                if (titleHighlight != null) {
                    String highlightText = titleHighlight.fragments()[0].string();
                    newsDoc.setNewsTitle(highlightText);
                }
                HighlightField subtitleHighlight = highlightFields.get("subtitle");
                if (subtitleHighlight != null) {
                    String subtitleText = subtitleHighlight.fragments()[0].string();
                    newsDoc.setSubtitle(subtitleText);
                }
                result.add(newsDoc);
            }
            // 查不到数据时 result 就是空列表
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            // 抛出带有错误信息的异常
            throw new ServiceException("ES获取新闻失败: " + e.getMessage());
        }
    }

    /**
     * 批量更新
     * @return
     */
    @Override
    public int updateToEs() {

        // 获取newsVO
        NewsVo newsVo = new NewsVo();
        List<NewsVo> list = appNewsMapper.selectNewsList(newsVo);

        // 1.创建Request
        BulkRequest bulkRequest = new BulkRequest();
        // 2.准备参数
        for (NewsVo newsVo1 : list) {
            NewsDoc newsDoc = new NewsDoc();
            BeanUtils.copyProperties(newsVo1, newsDoc);
            bulkRequest.add(new IndexRequest("news").id(newsDoc.getNewsId().toString())
                    .source(JSON.toJSONString(newsDoc), XContentType.JSON));
        }
        // 3.发送请求
        BulkResponse bulkResponse = null;
        try {
            bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);

            System.out.println(bulkResponse.hasFailures());
            return !bulkResponse.hasFailures() ? 1 : 0;
        } catch (IOException e) {
            e.printStackTrace();
            return 1;
        }

    }

    /**
     * 关键词补全
     *
     * @param prefix
     * @return
     */
    @Override
    public List<String> getSuggestions(String prefix) {
        SearchRequest searchRequest = new SearchRequest("searchbank");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // Suggest 查询
        SuggestBuilder suggestBuilder = new SuggestBuilder();
        CompletionSuggestionBuilder completionSuggestionBuilder = SuggestBuilders.completionSuggestion("objective")
                .prefix(prefix)
                .size(10);
        // 请求参数
        suggestBuilder.addSuggestion("objective-suggest",
                completionSuggestionBuilder);
        sourceBuilder.suggest(suggestBuilder);

        searchRequest.source(sourceBuilder);

        try {
            SearchResponse response = restHighLevelClient.search(searchRequest,
                    RequestOptions.DEFAULT);
            Suggest suggest = response.getSuggest();
            CompletionSuggestion suggestion = suggest.getSuggestion("objective-suggest");
            List<String> result = new ArrayList<>();
            for (CompletionSuggestion.Entry entry : suggestion.getEntries()) {
                for (CompletionSuggestion.Entry.Option option : entry) {
                    result.add(option.getText().string());
                }
            }
            return result;
        } catch (Exception e) {
            throw new ServiceException("ES自动补全失败: " + e.getMessage());
        }
    }

    @Override
    public List<String> getNewsHotKeys() {
        // 获取热门搜索关键词（前10个）
        Set<Object> hotKeywords = redisCache.zrevrange(CacheConstants.HOT_NEWS_SEARCH_KEYWORDS, 0, 9);
        // 转换为List<String>
        List<String> result = new ArrayList<>();
        if (hotKeywords != null) {
            for (Object keyword : hotKeywords) {
                if (keyword != null) {
                    result.add(keyword.toString());
                }
            }
        }
        return result;
    }

}
